package com.cy.pj.sys.service.aspect;

import com.cy.pj.common.annotation.RequiredLog;
import com.cy.pj.common.utils.IPUtils;
import com.cy.pj.sys.pojo.SysLog;
import com.cy.pj.sys.service.SysLogService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @Aspect 注解描述的类型为切面对象类型,此切面中可以定义多个切入点和通知方法.
 */
@Order
@Slf4j
@Aspect
@Component
public class SysLogAspect {
    /**
     * @Pointcut注解用于定义切入点
     * bean("spring容器中bean的名字")这个表达式为切入点表达式定义的一种语法,
     * 它描述的是某个bean或多个bean中所有方法的集合为切入点,这个形式的切入点
     * 表达式的缺陷是不能精确到具体方法的.
     */
     //@Pointcut("bean(sysUserServiceImpl)")
     @Pointcut("@annotation(com.cy.pj.common.annotation.RequiredLog)")
     public void doLog(){}//此方法只负责承载切入点的定义

    /**
     * @Around注解描述的方法,可以在切入点执行之前和之后进行业务拓展,
     * 在当前业务中,此方法为日志通知方法
     * @param jp 连接点对象,此对象封装了要执行的切入点方法信息.
     * 可以通过连接点对象调用目标方法.
     * @return 目标方法的执行结果
     * @throws Throwable
     */
     @Around("doLog()")
     public Object doAround(ProceedingJoinPoint jp)throws Throwable{

         long t1 = System.currentTimeMillis();
         log.info("Start:{}",t1);
         try {
             Object result = jp.proceed();//执行目标方法(切点方法中的某个方法)
             long t2 = System.currentTimeMillis();
             log.info("After:{}",t2);
             //保存用户正常行为日志(谁在什么时间，执行了什么操作，访问了什么方法，传递了什么参数，执行时长是多少)
            saveUserLog(jp,t2-t1);//--获取日志，调用日志service，调用日志dao，将数据写入到数据库
             return result;//目标业务方法的执行结果
         }catch(Throwable e){
             e.printStackTrace();
             log.error("Exception:{}",System.currentTimeMillis());
             throw e;
         }
     }
    @Autowired
    private SysLogService sysLogService;
     //记录用户行为记录
     private void  saveUserLog(ProceedingJoinPoint jp,long time) throws JsonProcessingException, NoSuchMethodException {
         //1,获取用户行为日志
         //1,1获取登录用户名(没做登录，可以给个固定值)
         String username = "cgb";
         //1,2获取ip地址
         String ip = IPUtils.getIpAddr();
         //1,3获取操作（operation）
         //1,3.1获取目标对象的类型
         Class<?> targetClass =
                 jp.getTarget().getClass();
         //1.3.2获取目标方法
         MethodSignature ms =
                 (MethodSignature) jp.getSignature();
         Method targetMethod =
                 targetClass.getMethod(ms.getName(),ms.getParameterTypes());
         //1.3.3获取方法上的RequiredLog注解
         RequiredLog annotation =
                 targetMethod.getAnnotation(RequiredLog.class);
         String operation = annotation.value();
         //1,4获取方法声明（类全名+方法名）
         String method =targetClass.getName()+targetMethod.getName();
         //1,5获取方法的实际参数信息
         Object[] args = jp.getArgs();
         String params = new ObjectMapper().writeValueAsString(args);

         //2,封装用户行为日志
         SysLog sysLog = new SysLog();
         sysLog.setIp(ip);
         sysLog.setUsername(username);
         sysLog.setOperation(operation);
         sysLog.setMethod(method);
         sysLog.setParams(params);
         sysLog.setTime((int)time);
        // sysLog.setCreatedTime();
         //3,存储用户信息日志到数据库
         sysLogService.saveObject(sysLog);
         /*new Thread(){
             @Override
             public void run() {
                 sysLogService.saveObject(sysLog);
             }
         }.start();*/
     }













  /*  @Autowired
    private SysLogService sysLogService;
    private void saveUserLog(ProceedingJoinPoint joinPoint,long time) throws NoSuchMethodException, JsonProcessingException {
        //1.获取用户行为日志
        //1.1获取访问用户的ip地址
        String ip= IPUtils.getIpAddr();
        //1.2获取登录用户的用户名
        SysUser user=(SysUser) SecurityUtils.getSubject().getPrincipal();
        String username=user.getUsername();//将来会用户登录用户名
        //1.3获取目标方法对象
        //获取目标对象类型(反射入口)
        Class<?> targetClass=joinPoint.getTarget().getClass();
        //获取目标对象类中的方法
        MethodSignature ms= (MethodSignature) joinPoint.getSignature();
        Method targetMethod=targetClass.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
        //1.4获取方法上的注解,进而获取注解中operation属性的值。
        RequiredLog requiredLog=targetMethod.getAnnotation(RequiredLog.class);
        String operation=requiredLog.operation();
        //1.5获取方法信息(方法所在类的类全名+方法名)
        String method=targetClass.getName()+"."+targetMethod.getName();//类全名+方法名
        //1.6 获取调用方法时传入的参数信息(假如参数可以转换为json格式，尽量转换为json格式)
        String params=new ObjectMapper().writeValueAsString(joinPoint.getArgs());
        //2.封装用户行为日志
        SysLog log=new SysLog();
        log.setIp(ip);
        log.setUsername(username);
        log.setOperation(operation);
        log.setMethod(method);
        log.setParams(params);
        log.setTime((int)time);
        log.setCreatedTime(new Date());
        //3.将日志写入到数据库
//        new Thread(){//线程对象的创建即耗时，又占用内存(一般一个线程1M内存)
//            @Override
//            public void run() {
//                sysLogService.saveObject(log);
//            }
//        }.start();

        sysLogService.saveObject(log);
    }*/
}

